package k8s

import (
	"flare-admin/http/model/k8sresource"
	"flare-admin/utils/common"
	"fmt"
	"sync"
	"time"

	"github.com/prometheus/common/model"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TransferPodModelToPod(pod *k8sresource.PodInfo) *corev1.Pod {
	p := &corev1.Pod{
		ObjectMeta: v1.ObjectMeta{
			Name:        pod.MetaData.Name,
			Namespace:   pod.MetaData.Namespace,
			Labels:      pod.MetaData.Labels,
			Annotations: pod.MetaData.Annotations,
		},
		Spec: corev1.PodSpec{
			Containers: make([]corev1.Container, 0),
		},
	}
	for _, container := range pod.Spec.Containers {
		p.Spec.Containers = append(p.Spec.Containers, corev1.Container{
			Name:  container.Name,
			Image: container.Image,
			Args:  container.Args,
			Ports: TransferPortModelToContainerPort(container.Ports),
			Env:   TransferEnvModelToEnvVar(container.Env),
			Resources: corev1.ResourceRequirements{
				Requests: TransferResourceRequirementsToResourceList(container.Resources.Requests),
				Limits:   TransferResourceRequirementsToResourceList(container.Resources.Limits),
			},
		})
	}
	return p
}

func TransferPortModelToContainerPort(ports []k8sresource.ContainerPort) []corev1.ContainerPort {
	res := make([]corev1.ContainerPort, 0, len(ports))
	for _, port := range ports {
		res = append(res, corev1.ContainerPort{
			Name:          port.Name,
			ContainerPort: int32(port.ContainerPort),
			Protocol:      corev1.Protocol(port.Protocol),
		})
	}
	return res
}

func TransferEnvModelToEnvVar(envs []k8sresource.EnvVar) []corev1.EnvVar {
	res := make([]corev1.EnvVar, 0, len(envs))
	for _, env := range envs {
		res = append(res, corev1.EnvVar{
			Name:  env.Name,
			Value: env.Value,
		})
	}
	return res
}

func TransferResourceRequirementsToResourceList(reqs map[string]string) corev1.ResourceList {
	res := make(corev1.ResourceList)
	for k, v := range reqs {
		if v != "" {
			res[corev1.ResourceName(k)] = resource.MustParse(v) // 传值不正确会导致panic,前端一定要保证好！
		}
	}
	return res
}

func TransferPodToPodModel(pod *corev1.Pod) *k8sresource.PodInfo {
	p := &k8sresource.PodInfo{
		MetaData: k8sresource.Metadata{
			Name:              pod.ObjectMeta.Name,
			Namespace:         pod.ObjectMeta.Namespace,
			UID:               string(pod.ObjectMeta.UID),
			Labels:            pod.ObjectMeta.Labels,
			CreationTimestamp: pod.ObjectMeta.CreationTimestamp.Format(time.RFC3339),
			Annotations:       pod.ObjectMeta.Annotations,
		},
		Spec: k8sresource.PodSpec{
			NodeName:   pod.Spec.NodeName,
			Containers: make([]k8sresource.Container, 0),
		},
		Status: k8sresource.PodStatus{
			Phase:             string(pod.Status.Phase),
			PodIP:             pod.Status.PodIP,
			ContainerStatuses: make([]k8sresource.ContainerStatus, 0),
			DurationTime:      common.FormatDurationSinceNow(pod.Status.StartTime.Time),
		},
	}
	for _, container := range pod.Spec.Containers {
		p.Spec.Containers = append(p.Spec.Containers, k8sresource.Container{
			Name:  container.Name,
			Image: container.Image,
			Args:  container.Args,
			Ports: TransferContainerPortToPortModel(container.Ports),
			Env:   TransferEnvVarToEnvModel(container.Env),
			Resources: k8sresource.Resources{
				Requests: TransferResourceListToResourceRequirements(container.Resources.Requests),
				Limits:   TransferResourceListToResourceRequirements(container.Resources.Limits),
			},
		})
	}

	for _, containerStatus := range pod.Status.ContainerStatuses {
		p.Status.ContainerStatuses = append(p.Status.ContainerStatuses, k8sresource.ContainerStatus{
			Ready: containerStatus.Ready,
		})
	}
	return p
}
func TransferContainerPortToPortModel(ports []corev1.ContainerPort) []k8sresource.ContainerPort {
	res := make([]k8sresource.ContainerPort, 0, len(ports))
	for _, port := range ports {
		res = append(res, k8sresource.ContainerPort{
			Name:          port.Name,
			ContainerPort: int(port.ContainerPort),
			Protocol:      string(port.Protocol),
		})
	}
	return res
}
func TransferEnvVarToEnvModel(envs []corev1.EnvVar) []k8sresource.EnvVar {
	res := make([]k8sresource.EnvVar, 0, len(envs))
	for _, env := range envs {
		res = append(res, k8sresource.EnvVar{
			Name:  env.Name,
			Value: env.Value,
		})
	}
	return res
}
func TransferResourceListToResourceRequirements(reqs corev1.ResourceList) map[string]string {
	res := make(map[string]string)
	for k, v := range reqs {
		if v.String() != "" {
			res[string(k)] = v.String() // 传值不正确会导致panic,前端一定要保证好！
		}
	}
	return res
}

func TransferDeploymentToDeploymentModel(deployment *appsv1.Deployment) *k8sresource.DeploymentInfo {
	d := &k8sresource.DeploymentInfo{
		MetaData: k8sresource.Metadata{
			Name:              deployment.ObjectMeta.Name,
			Namespace:         deployment.ObjectMeta.Namespace,
			UID:               string(deployment.ObjectMeta.UID),
			Labels:            deployment.ObjectMeta.Labels,
			CreationTimestamp: deployment.ObjectMeta.CreationTimestamp.Format(time.RFC3339),
			Annotations:       deployment.ObjectMeta.Annotations,
		},
		Spec: k8sresource.DeploymentSpec{
			// Replicas: *deployment.Spec.Replicas,
			Replicas: GetReplicas(deployment.Spec.Replicas, 1), // Default to 1 if not set
			Selector: k8sresource.LabelSelector{
				MatchLabels: deployment.Spec.Selector.MatchLabels,
			},
			Strategy: k8sresource.DeploymentStrategy{
				Type: string(deployment.Spec.Strategy.Type), // e.g., "RollingUpdate",
			},
			Template: k8sresource.PodTemplateSpec{
				Metadata: k8sresource.Metadata{
					Name:        deployment.Spec.Template.ObjectMeta.Name,
					Labels:      deployment.Spec.Template.ObjectMeta.Labels,
					Annotations: deployment.Spec.Template.ObjectMeta.Annotations,
				},
				Spec: k8sresource.PodSpec{
					NodeName:   deployment.Spec.Template.Spec.NodeName,
					Containers: make([]k8sresource.Container, 0),
				},
			},
		},
		Status: k8sresource.DeploymentStatus{
			Replicas:            deployment.Status.Replicas,
			ReadyReplicas:       deployment.Status.ReadyReplicas,
			AvailableReplicas:   deployment.Status.AvailableReplicas,
			UnavailableReplicas: deployment.Status.UnavailableReplicas,
			DurationTime:        common.FormatDurationSinceNow(deployment.ObjectMeta.CreationTimestamp.Time),
			Conditions:          make([]k8sresource.Condition, 0),
		},
	}

	for _, container := range deployment.Spec.Template.Spec.Containers {
		d.Spec.Template.Spec.Containers = append(d.Spec.Template.Spec.Containers, k8sresource.Container{
			Name:  container.Name,
			Image: container.Image,
			Args:  container.Args,
			Ports: TransferContainerPortToPortModel(container.Ports),
			Env:   TransferEnvVarToEnvModel(container.Env),
			Resources: k8sresource.Resources{
				Requests: TransferResourceListToResourceRequirements(container.Resources.Requests),
				Limits:   TransferResourceListToResourceRequirements(container.Resources.Limits),
			},
		})
	}

	for _, condition := range deployment.Status.Conditions {
		d.Status.Conditions = append(d.Status.Conditions, k8sresource.Condition{
			Type:           string(condition.Type),
			Status:         string(condition.Status),
			LastUpdateTime: common.FormatDurationSinceNow(condition.LastUpdateTime.Time),
			Reason:         condition.Reason,
		})
	}
	return d
}

func GetReplicas(replicas *int32, defaultVal int32) int32 {
	if replicas != nil {
		return *replicas
	}
	return defaultVal
}

func TransferDeploymentModelToDeployment(deployment *k8sresource.DeploymentInfo) *appsv1.Deployment {
	d := &appsv1.Deployment{
		ObjectMeta: v1.ObjectMeta{
			Name:        deployment.MetaData.Name,
			Namespace:   deployment.MetaData.Namespace,
			Labels:      deployment.MetaData.Labels,
			Annotations: deployment.MetaData.Annotations,
		},
		Spec: appsv1.DeploymentSpec{
			Replicas: &deployment.Spec.Replicas,
			Selector: &v1.LabelSelector{
				MatchLabels: deployment.Spec.Selector.MatchLabels,
			},
			Strategy: appsv1.DeploymentStrategy{
				Type: appsv1.DeploymentStrategyType(deployment.Spec.Strategy.Type),
			},
			Template: corev1.PodTemplateSpec{
				ObjectMeta: v1.ObjectMeta{
					// Name:        deployment.Spec.Template.Metadata.Name,
					Labels:      deployment.Spec.Template.Metadata.Labels,
					Annotations: deployment.Spec.Template.Metadata.Annotations,
				},
				Spec: corev1.PodSpec{
					NodeName:   deployment.Spec.Template.Spec.NodeName,
					Containers: make([]corev1.Container, 0),
				},
			},
		},
	}

	for _, container := range deployment.Spec.Template.Spec.Containers {
		d.Spec.Template.Spec.Containers = append(d.Spec.Template.Spec.Containers, corev1.Container{
			Name:  container.Name,
			Image: container.Image,
			Args:  container.Args,
			Ports: TransferPortModelToContainerPort(container.Ports),
			Env:   TransferEnvModelToEnvVar(container.Env),
			Resources: corev1.ResourceRequirements{
				Requests: TransferResourceRequirementsToResourceList(container.Resources.Requests),
				Limits:   TransferResourceRequirementsToResourceList(container.Resources.Limits),
			},
		})
	}

	return d
}

func TransferPrometheusLatencyInfoToLatencyInfo(promLatencyInfo model.Matrix) (res LatencyInfo) {
	res.NodeLatencyToOthers = make(map[string][]NodeLatency)
	var timeStampList []time.Time
	so := sync.Once{}
	buildTimestampList := true
	for _, sample := range promLatencyInfo {
		src := string(sample.Metric["src"])
		dst := string(sample.Metric["dst"])
		key := fmt.Sprintf("%s||%s", src, dst)

		so.Do(func() {
			timeStampList = make([]time.Time, len(sample.Values))
		})

		appendLatencyInfo(sample, key, res, timeStampList, &buildTimestampList)
	}
	res.TimeStampList = timeStampList
	return
}

// appendLatencyInfo 将单个样本流的数据追加到 LatencyInfo 结构中，
// 我们希望将timestamp单独抽出（所有的指标其实只需要一个共享的timestamp数组就够了，不需要每个NodeLatency中都存储一个timestamp）
// timeStampList []time.Time 传入的时间戳列表，需要事先构造好大小，本函数中直接使用下标的方式进行构造
func appendLatencyInfo(samples *model.SampleStream, key string, lf LatencyInfo, timestampList []time.Time, buildTimestampList *bool) {
	if *buildTimestampList {
		for i, v := range samples.Values {
			timestampList[i] = v.Timestamp.Time()
			lf.NodeLatencyToOthers[key] = append(lf.NodeLatencyToOthers[key],
				NodeLatency{
					Latency: float64(v.Value),
					Unit:    "ms",
					//TimeStamp: v.Timestamp, // 此处不需要
				},
			)
		}
		*buildTimestampList = false
	} else {
		for _, v := range samples.Values {
			lf.NodeLatencyToOthers[key] = append(lf.NodeLatencyToOthers[key],
				NodeLatency{
					Latency: float64(v.Value),
					Unit:    "ms",
					//TimeStamp: v.Timestamp, // 此处不需要
				},
			)
		}
	}
}
